package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/zeromicro/go-zero/core/logx"
	"gorm.io/gorm"
	"gozero-chat-example/model"
)

// WebSocket服务器
type WebSocketServer struct {
	Rooms map[string]*Room
	mu    sync.RWMutex
	// 用于升级HTTP连接到WebSocket
	upgrader websocket.Upgrader
	DB       *gorm.DB
}

// 创建新的WebSocket服务器
func NewWebSocketServer(db *gorm.DB) *WebSocketServer {
	// 设置数据库连接池
	sqlDB, _ := db.DB()
	sqlDB.SetMaxIdleConns(10)
	sqlDB.SetMaxOpenConns(100)
	sqlDB.SetConnMaxLifetime(time.Hour)

	return &WebSocketServer{
		Rooms: make(map[string]*Room),
		upgrader: websocket.Upgrader{
			CheckOrigin: func(r *http.Request) bool {
				return true // 允许所有来源
			},
			ReadBufferSize:  1024,
			WriteBufferSize: 1024,
		},
		DB: db,
	}
}

// 获取房间（不自动创建）
func (s *WebSocketServer) getRoom(id string) *Room {
	s.mu.RLock()
	room, exists := s.Rooms[id]
	s.mu.RUnlock()
	if exists {
		return room
	}
	return nil
}

// 新增：房间列表API
func (s *WebSocketServer) ServeRoomList(w http.ResponseWriter, r *http.Request) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	var rooms []RoomInfo
	for _, room := range s.Rooms {
		rooms = append(rooms, RoomInfo{
			ID:       room.ID,
			Name:     room.Name,
			NumUsers: len(room.Clients),
			MaxUsers: room.MaxUsers,
		})
	}
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(rooms)
}

// 修改：创建房间支持自定义MaxUsers
func (s *WebSocketServer) CreateRoom(name, creatorID, creatorName string, maxUsers int) string {
	roomID := fmt.Sprintf("room-%d", time.Now().UnixNano())
	s.mu.Lock()
	defer s.mu.Unlock()
	room := &Room{
		ID:          roomID,
		Name:        name,
		CreatorID:   creatorID,
		CreatorName: creatorName,
		Clients:     make(map[string]*Client),
		MaxUsers:    maxUsers,
		wsServer:    s, // 赋值
	}
	s.Rooms[roomID] = room
	return roomID
}

// 获取所有房间
func (s *WebSocketServer) GetRooms() map[string]*Room {
	s.mu.RLock()
	defer s.mu.RUnlock()

	// 创建一个副本，避免并发问题
	rooms := make(map[string]*Room, len(s.Rooms))
	for id, room := range s.Rooms {
		rooms[id] = room
	}

	return rooms
}

// 向房间添加客户端
func (r *Room) addClient(client *Client, _ *WebSocketServer) {
	r.mu.Lock()
	r.Clients[client.UserID] = client
	if r.UserNames == nil {
		r.UserNames = make(map[string]string)
	}
	if r.JoinTimes == nil {
		r.JoinTimes = make(map[string]int64)
	}
	if r.Ready == nil {
		r.Ready = make(map[string]bool)
	}
	// 只在首次加入时赋值 join_time，避免被覆盖
	if _, ok := r.JoinTimes[client.UserID]; !ok {
		r.JoinTimes[client.UserID] = time.Now().UnixNano()
	}
	r.UserNames[client.UserID] = client.UserName // 始终更新用户名，防止为空
	if _, ok := r.Ready[client.UserID]; !ok {
		r.Ready[client.UserID] = false // 初始状态为未准备
	}
	if r.destroyTimer != nil {
		r.destroyTimer.Stop()
		r.destroyTimer = nil
	}
	r.mu.Unlock()
	// 清空empty_since
	if r.wsServer != nil && r.wsServer.DB != nil {
		if err := model.ClearRoomEmptySince(r.wsServer.DB, r.ID); err != nil {
			logx.Errorf("清除房间empty_since失败: %v", err)
		}
	}
	logx.Infof("[addClient] user %s(%s) joined room %s, now %d users", client.UserName, client.UserID, r.ID, len(r.Clients))
}

// 从房间移除客户端
func (r *Room) removeClient(client *Client, wsServer *WebSocketServer) {
	if client == nil {
		logx.Error("[removeClient] 尝试移除空客户端")
		return
	}

	// 先获取必要信息，避免锁内执行过多操作
	userID := client.UserID
	userName := client.UserName

	r.mu.Lock()
	if _, exists := r.Clients[userID]; exists {
		delete(r.Clients, userID)
		delete(r.UserNames, userID)
		delete(r.JoinTimes, userID)
		delete(r.Ready, userID)
	}
	empty := len(r.Clients) == 0
	clientCount := len(r.Clients)
	r.mu.Unlock()

	// 安全关闭客户端连接和通道
	// 在锁外执行，避免死锁
	if client != nil && client.Send != nil {
		// 使用客户端锁保护关闭操作
		client.mu.Lock()
		select {
		case <-client.Send:
			// 通道已关闭
		default:
			close(client.Send)
		}

		// 安全关闭WebSocket连接
		if client.Conn != nil {
			client.Conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "用户离开房间"))
			client.Conn.Close()
		}
		client.mu.Unlock()
	}

	logx.Infof("[removeClient] user %s(%s) left room %s, now %d users", userName, userID, r.ID, clientCount)

	// 延迟一小段时间再广播，确保连接已正确关闭
	time.Sleep(100 * time.Millisecond)

	// 通知房间内其他用户有用户离开
	leaveMsg := Message{
		Type:     MessageTypeLeave,
		UserID:   userID,
		UserName: userName,
		Content:  fmt.Sprintf("%s 离开了房间", userName),
		Time:     time.Now().Unix(),
	}
	r.broadcast(leaveMsg)

	// 自动销毁逻辑
	if empty {
		// 设置empty_since
		if r.wsServer != nil && r.wsServer.DB != nil {
			if err := model.SetRoomEmptySince(r.wsServer.DB, r.ID, time.Now()); err != nil {
				logx.Errorf("设置房间empty_since失败: %v", err)
			}
		}
		r.mu.Lock()
		// 检查是否真的没有客户端
		if len(r.Clients) == 0 {
			if r.destroyTimer == nil {
				logx.Infof("[auto-destroy] 设置房间 %s 的自动销毁计时器（30秒）", r.ID)
				r.destroyTimer = time.AfterFunc(30*time.Second, func() {
					// 再次检查房间是否真的为空
					r.mu.Lock()
					if len(r.Clients) > 0 {
						logx.Infof("[auto-destroy] 房间 %s 不再为空，取消自动销毁", r.ID)
						r.mu.Unlock()
						return
					}
					r.mu.Unlock()

					logx.Infof("[auto-destroy] room %s will be destroyed (no users)", r.ID)
					if r.wsServer != nil {
						r.wsServer.mu.Lock()
						delete(r.wsServer.Rooms, r.ID)
						r.wsServer.mu.Unlock()

						// 从数据库中删除房间
						if r.wsServer.DB != nil {
							if err := model.DeleteRoomByID(r.wsServer.DB, r.ID); err != nil {
								logx.Errorf("[auto-destroy] failed to delete room %s from database: %v", r.ID, err)
							} else {
								logx.Infof("[auto-destroy] room %s deleted from database", r.ID)
							}
						}
					}
					logx.Infof("[auto-destroy] room %s destroyed", r.ID)
				})
			}
		} else {
			// 如果检查发现其实有客户端，取消销毁计时器
			logx.Infof("[auto-destroy] 房间 %s 不为空 (客户端数: %d)，取消自动销毁", r.ID, len(r.Clients))
			if r.destroyTimer != nil {
				r.destroyTimer.Stop()
				r.destroyTimer = nil
			}
		}
		r.mu.Unlock()
	}

	// 在 removeClient 里，移除用户后加上：
	if wsServer != nil && wsServer.DB != nil {
		// 清理用户的准备状态和房间ID
		if err := model.UpdateUserReadyStatus(wsServer.DB, userID, "", false); err != nil {
			logx.Errorf("更新用户准备状态失败: %v", err)
		}
	}
}

// 向房间内所有客户端广播消息
func (r *Room) broadcast(message Message) {
	// 预先序列化消息
	data, err := json.Marshal(message)
	if err != nil {
		logx.Errorf("消息序列化失败: %v", err)
		return
	}

	r.mu.RLock()

	// 创建客户端列表副本，避免在迭代过程中出现并发问题
	clients := make(map[string]*Client, len(r.Clients))
	for id, client := range r.Clients {
		clients[id] = client
	}

	r.mu.RUnlock()

	// 日志记录广播信息
	log.Printf("广播消息: 类型=%d, 用户ID=%s, 客户端数量=%d, 内容=%s",
		message.Type, message.UserID, len(clients), message.Content)

	// 特别处理准备状态变更消息
	if message.Type == MessageTypeReadyChanged {
		log.Printf("准备状态变更消息广播开始 - 用户ID=%s, 内容=%s",
			message.UserID, message.Content)

		// 尝试解析准备状态内容
		var readyData map[string]interface{}
		if err := json.Unmarshal([]byte(message.Content), &readyData); err == nil {
			log.Printf("准备状态详情: 用户ID=%v, 用户名=%v, 准备状态=%v",
				readyData["user_id"], readyData["user_name"], readyData["ready"])
		}
	}

	// 向所有客户端发送消息
	for clientID, client := range clients {
		// 确保客户端连接有效
		if client == nil || client.Conn == nil {
			log.Printf("客户端 %s 连接无效，跳过发送", clientID)
			continue
		}
		// 跳过原始发送者
		if clientID == message.UserID {
			continue
		}

		// 使用非阻塞发送，避免阻塞广播过程
		select {
		case client.Send <- data:
			// 特别记录准备状态变更消息的发送
			if message.Type == MessageTypeReadyChanged {
				log.Printf("准备状态变更消息已发送到客户端: %s", clientID)
			} else {
				log.Printf("消息已发送到客户端: %s", clientID)
			}
		default:
			// 如果发送队列已满，记录日志但不关闭连接
			log.Printf("客户端 %s 发送队列已满，消息可能丢失", clientID)
		}
	}

	// 特别处理准备状态变更消息
	if message.Type == MessageTypeReadyChanged {
		log.Printf("准备状态变更消息广播完成")
	}
}

// 客户端读取消息循环
func (c *Client) readPump(room *Room, wsServer *WebSocketServer) {
	defer func() {
		// 捕获可能的panic
		if r := recover(); r != nil {
			log.Printf("readPump恢复自panic: %v", r)
		}

		// 确保连接关闭
		c.mu.Lock()
		if c.Conn != nil {
			c.Conn.Close()
			c.Conn = nil
		}
		c.mu.Unlock()

		// 安全移除客户端
		room.removeClient(c, wsServer)
	}()

	// 设置读取限制和超时
	c.mu.Lock()
	if c.Conn != nil {
		c.Conn.SetReadLimit(512)
		c.Conn.SetReadDeadline(time.Now().Add(60 * time.Second))
		c.Conn.SetPongHandler(func(string) error {
			c.Conn.SetReadDeadline(time.Now().Add(60 * time.Second))
			return nil
		})
	}
	c.mu.Unlock()

	for {
		// 安全读取消息
		c.mu.Lock()
		if c.Conn == nil {
			c.mu.Unlock()
			log.Printf("连接已关闭，停止读取消息")
			break
		}

		_, message, err := c.Conn.ReadMessage()
		c.mu.Unlock()

		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				log.Printf("WebSocket读取错误: %v", err)
			}
			break
		}

		log.Printf("收到WebSocket消息: %s", string(message))

		// 解析消息
		var msg Message
		if err := json.Unmarshal(message, &msg); err != nil {
			log.Printf("消息解析失败: %v, 原始消息: %s", err, string(message))
			continue
		}

		// 设置消息发送者和时间
		msg.UserID = c.UserID
		msg.UserName = c.UserName
		msg.Time = time.Now().Unix()

		if msg.Type == MessageTypeText {
			room.addMessage(&msg)
		}
		if msg.Type == MessageTypeGameOp {
			log.Printf("收到游戏操作消息: %s", msg.Content)
			var op SnakeGameOp
			if err := json.Unmarshal([]byte(msg.Content), &op); err == nil {
				// 保留消息中可能包含的用户ID，但如果没有则使用连接的用户ID
				if op.UserID == "" {
					op.UserID = c.UserID
				}
				log.Printf("解析游戏操作: 用户=%s, 操作=%s", op.UserID, op.Op)

				// 特殊处理准备状态变更，确保广播消息
				if op.Op == "ready" {
					log.Printf("处理准备状态变更操作: 用户=%s", op.UserID)
				}

				// 使用goroutine处理游戏操作，避免阻塞读取循环
				go func(operation SnakeGameOp) {
					defer func() {
						if r := recover(); r != nil {
							log.Printf("处理游戏操作时恢复自panic: %v", r)
						}
					}()
					room.handleGameOp(operation)
				}(op)
			} else {
				log.Printf("游戏操作解析失败: %v, 内容: %s", err, msg.Content)
			}
			continue
		}

		// 广播消息到房间
		go room.broadcast(msg) // 使用goroutine异步广播，避免阻塞读取循环
	}
}

// 客户端写入消息循环
func (c *Client) writePump() {
	ticker := time.NewTicker(30 * time.Second)
	defer func() {
		ticker.Stop()
		// 不要在这里关闭连接，避免过早关闭
		// c.Conn.Close()
	}()

	// 添加互斥锁，防止并发写入
	var writeMu sync.Mutex

	for {
		select {
		case message, ok := <-c.Send:
			if !ok {
				// 发送通道已关闭
				log.Printf("客户端发送通道已关闭: %s", c.UserID)

				// 加锁确保安全写入
				writeMu.Lock()
				// 发送关闭消息
				c.Conn.SetWriteDeadline(time.Now().Add(20 * time.Second))
				c.Conn.WriteMessage(websocket.CloseMessage, []byte{})
				writeMu.Unlock()

				// 等待一段时间再返回
				time.Sleep(100 * time.Millisecond)
				return
			}

			// 检查是否是准备状态变更消息
			var msg Message
			if err := json.Unmarshal(message, &msg); err == nil && msg.Type == MessageTypeReadyChanged {
				log.Printf("准备发送准备状态变更消息到客户端 %s: %s", c.UserID, string(message))
			}

			log.Printf("向客户端 %s 写入消息: %s", c.UserID, string(message))

			// 加锁确保安全写入
			writeMu.Lock()

			// 检查连接是否有效
			if c.Conn == nil {
				log.Printf("客户端 %s 连接已关闭，无法写入消息", c.UserID)
				writeMu.Unlock()
				return
			}

			// 设置写入超时
			c.Conn.SetWriteDeadline(time.Now().Add(20 * time.Second))

			// 尝试获取写入器
			w, err := c.Conn.NextWriter(websocket.TextMessage)
			if err != nil {
				writeMu.Unlock() // 错误情况下解锁
				log.Printf("获取写入器失败: %v，尝试重试", err)

				// 短暂延迟后重试一次
				time.Sleep(100 * time.Millisecond)

				writeMu.Lock() // 重新加锁

				// 检查连接是否有效
				if c.Conn == nil {
					log.Printf("客户端 %s 连接已关闭，无法重试写入", c.UserID)
					writeMu.Unlock()
					return
				}

				c.Conn.SetWriteDeadline(time.Now().Add(20 * time.Second))
				w, err = c.Conn.NextWriter(websocket.TextMessage)
				if err != nil {
					writeMu.Unlock() // 错误情况下解锁
					log.Printf("重试获取写入器仍然失败: %v", err)
					continue // 不要返回，继续尝试处理其他消息
				}
			}

			// 写入消息
			_, err = w.Write(message)
			if err != nil {
				writeMu.Unlock() // 错误情况下解锁
				log.Printf("写入消息失败: %v", err)
				continue // 不要返回，继续尝试处理其他消息
			}

			// 添加上次写入的缓冲区中的任何消息
			n := len(c.Send)
			for i := 0; i < n; i++ {
				select {
				case msg := <-c.Send:
					w.Write([]byte{'\n'})
					w.Write(msg)
				default:
					// 通道可能已关闭，避免阻塞
					break
				}
			}

			// 关闭写入器
			if err := w.Close(); err != nil {
				writeMu.Unlock() // 错误情况下解锁
				log.Printf("关闭写入器失败: %v", err)
				continue // 不要返回，继续尝试处理其他消息
			}

			// 完成写入，解锁
			writeMu.Unlock()

			// 检查是否是准备状态变更消息
			if err := json.Unmarshal(message, &msg); err == nil && msg.Type == MessageTypeReadyChanged {
				log.Printf("准备状态变更消息已成功写入客户端 %s", c.UserID)
			} else {
				log.Printf("消息已成功写入客户端 %s", c.UserID)
			}

		case <-ticker.C:
			// 加锁确保安全写入
			writeMu.Lock()

			// 发送Ping消息保持连接
			c.Conn.SetWriteDeadline(time.Now().Add(20 * time.Second))
			if err := c.Conn.WriteMessage(websocket.PingMessage, nil); err != nil {
				writeMu.Unlock() // 错误情况下解锁
				log.Printf("发送Ping消息失败: %v，可能连接已断开", err)

				// 不要立即返回，给一次重连机会
				time.Sleep(1 * time.Second)

				writeMu.Lock() // 重新加锁
				c.Conn.SetWriteDeadline(time.Now().Add(20 * time.Second))
				if err := c.Conn.WriteMessage(websocket.PingMessage, nil); err != nil {
					writeMu.Unlock() // 错误情况下解锁
					log.Printf("重试发送Ping消息仍然失败，连接可能已断开: %v", err)
					return
				}
				writeMu.Unlock() // 成功情况下解锁
			} else {
				writeMu.Unlock() // 成功情况下解锁
			}
		}
	}
}

// Room 添加消息并返回消息ID
func (r *Room) addMessage(msg *Message) string {
	r.mu.RLock()
	defer r.mu.RUnlock()
	msg.ID = fmt.Sprintf("msg-%d", time.Now().UnixNano())
	if msg.ReadBy == nil {
		msg.ReadBy = make(map[string]bool)
	}
	if msg.UserName == "" {
		msg.UserName = r.UserNames[msg.UserID]
	}
	r.Messages = append(r.Messages, msg)
	return msg.ID
}

// 获取历史消息，支持 before 和 limit
func (r *Room) getMessages(beforeID string, limit int) []*Message {
	r.mu.RLock()
	defer r.mu.RUnlock()
	if len(r.Messages) == 0 {
		return nil
	}
	end := len(r.Messages)
	if beforeID != "" {
		for i := len(r.Messages) - 1; i >= 0; i-- {
			if r.Messages[i].ID == beforeID {
				end = i
				break
			}
		}
	}
	start := end - limit
	if start < 0 {
		start = 0
	}
	return r.Messages[start:end]
}

// getUserList 返回房间内所有在线用户的列表（只遍历 Clients）
func (r *Room) getUserList() []map[string]interface{} {
	r.mu.RLock()
	defer r.mu.RUnlock()

	users := make([]map[string]interface{}, 0, len(r.Clients))
	for userID, client := range r.Clients {
		userData := map[string]interface{}{
			"user_id":   userID,
			"user_name": client.UserName,
		}

		// 添加加入时间（如果存在）
		if joinTime, ok := r.JoinTimes[userID]; ok {
			userData["join_time"] = joinTime
		} else {
			userData["join_time"] = time.Now().UnixNano()
		}

		// 添加准备状态（如果存在）
		if ready, ok := r.Ready[userID]; ok {
			userData["ready"] = ready
		} else {
			userData["ready"] = false
		}

		users = append(users, userData)
	}

	// 按加入时间排序
	sort.Slice(users, func(i, j int) bool {
		return users[i]["join_time"].(int64) < users[j]["join_time"].(int64)
	})

	return users
}

// 设置消息已读
func (r *Room) setMessageRead(msgID, userID string) bool {
	r.mu.Lock()
	defer r.mu.Unlock()
	for _, msg := range r.Messages {
		if msg.ID == msgID {
			if msg.ReadBy == nil {
				msg.ReadBy = make(map[string]bool)
			}
			msg.ReadBy[userID] = true
			return true
		}
	}
	return false
}

// 删除房间时校验权限
func (s *WebSocketServer) DeleteRoom(roomID, userID string) bool {
	s.mu.Lock()
	defer s.mu.Unlock()
	rm, ok := s.Rooms[roomID]
	if !ok {
		logx.Infof("[DeleteRoom] room %s not found, user %s", roomID, userID)
		return false
	}
	// 允许房主随时删除，或房间人数为0时任何人都可删除
	rm.mu.RLock()
	clientCount := len(rm.Clients)
	rm.mu.RUnlock()
	logx.Infof("[DeleteRoom] user %s try delete room %s, creator=%s, userCount=%d", userID, roomID, rm.CreatorID, clientCount)
	if rm.CreatorID != userID && clientCount > 0 {
		logx.Infof("[DeleteRoom] no permission: user %s, creator %s, userCount %d", userID, rm.CreatorID, clientCount)
		return false
	}
	delete(s.Rooms, roomID)
	logx.Infof("[DeleteRoom] room %s deleted by user %s", roomID, userID)
	return true
}

// 获取或创建可加入的snake房间（每个房间最多2人）
func (s *WebSocketServer) GetOrCreateSnakeRoomForJoin() *Room {
	s.mu.Lock()
	defer s.mu.Unlock()
	// 查找未满的snake房间
	for _, room := range s.Rooms {
		if len(room.Clients) < room.MaxUsers && room.Name == "snake" {
			return room
		}
	}
	// 没有未满房间则新建
	roomID := fmt.Sprintf("snake-%d", time.Now().UnixNano())
	room := &Room{
		ID:       roomID,
		Name:     "snake",
		Clients:  make(map[string]*Client),
		MaxUsers: 2,
	}
	s.Rooms[roomID] = room
	return room
}

// 玩家操作消息结构
// op: "ready", "start", "direction", "sync_state"
type SnakeGameOp struct {
	Op       string `json:"op"`
	UserID   string `json:"user_id"`
	UserName string `json:"user_name,omitempty"` // 添加用户名字段
	Dir      string `json:"dir,omitempty"`
	Ready    *bool  `json:"ready,omitempty"`
}

// 创建默认蛇
func defaultSnake(x, y int, dir string) *Snake {
	return &Snake{
		Segments:  []SnakeSegment{{X: x, Y: y}},
		Direction: dir,
		Alive:     true,
	}
}

func (r *Room) StartGame() {
	r.mu.Lock()
	defer r.mu.Unlock()

	log.Printf("开始游戏，房间ID=%s，玩家数=%d", r.ID, len(r.Clients))

	if r.GameState == nil {
		r.GameState = &SnakeGameState{
			Snakes: make(map[string]*Snake),
			Foods:  []SnakeSegment{},
			Scores: make(map[string]int),
			Ready:  make(map[string]bool),
			Status: "playing",
		}
	}

	// 初始化所有玩家蛇
	idx := 0
	for uid, client := range r.Clients {
		r.GameState.Snakes[uid] = defaultSnake(2+idx*3, 2+idx*3, "RIGHT")
		r.GameState.Scores[uid] = 0
		r.GameState.Ready[uid] = false
		log.Printf("初始化玩家蛇: 用户ID=%s, 用户名=%s, 索引=%d", uid, client.UserName, idx)
		idx++
	}

	// 初始化食物
	r.GameState.Foods = []SnakeSegment{r.randomFood()}
	r.GameState.Status = "playing"

	// 确保停止之前的游戏循环（如果有）
	if r.stopGame != nil {
		close(r.stopGame)
	}
	r.stopGame = make(chan struct{})

	// 立即广播一次游戏状态，确保所有客户端同步
	r.broadcastGameState()

	// 启动游戏循环
	r.gameTicker = time.NewTicker(200 * time.Millisecond)
	go r.gameLoop()

	log.Printf("游戏已开始，房间ID=%s", r.ID)
}

func (r *Room) StopGame() {
	r.mu.Lock()
	defer r.mu.Unlock()
	if r.gameTicker != nil {
		r.gameTicker.Stop()
	}
	if r.stopGame != nil {
		close(r.stopGame)
	}
	if r.GameState != nil {
		r.GameState.Status = "gameover"
	}
	r.broadcastGameState()
}

func (r *Room) randomFood() SnakeSegment {
	return SnakeSegment{
		X: 1 + int(time.Now().UnixNano()%14),
		Y: 1 + int(time.Now().UnixNano()%14),
	}
}

func (r *Room) gameLoop() {
	log.Printf("游戏循环开始，房间ID=%s", r.ID)

	for {
		select {
		case <-r.gameTicker.C:
			r.mu.Lock()
			if r.GameState == nil || r.GameState.Status != "playing" {
				log.Printf("游戏状态无效或不是playing状态，退出游戏循环")
				r.mu.Unlock()
				return
			}

			// 推进所有蛇
			aliveCount := 0
			for uid, snake := range r.GameState.Snakes {
				if !snake.Alive {
					continue
				}
				head := snake.Segments[0]
				dx, dy := 0, 0
				switch snake.Direction {
				case "LEFT":
					dx = -1
				case "RIGHT":
					dx = 1
				case "UP":
					dy = -1
				case "DOWN":
					dy = 1
				}
				newHead := SnakeSegment{X: head.X + dx, Y: head.Y + dy}
				// 撞墙
				if newHead.X < 0 || newHead.X > 15 || newHead.Y < 0 || newHead.Y > 15 {
					snake.Alive = false
					log.Printf("玩家 %s 撞墙，游戏结束", uid)
					continue
				}
				// 撞自己
				for _, seg := range snake.Segments[1:] {
					if seg.X == newHead.X && seg.Y == newHead.Y {
						snake.Alive = false
						log.Printf("玩家 %s 撞到自己，游戏结束", uid)
						break
					}
				}
				if !snake.Alive {
					continue
				}
				// 撞其他蛇
				for oid, osnake := range r.GameState.Snakes {
					if oid == uid {
						continue
					}
					for _, seg := range osnake.Segments {
						if seg.X == newHead.X && seg.Y == newHead.Y {
							snake.Alive = false
							log.Printf("玩家 %s 撞到玩家 %s，游戏结束", uid, oid)
							break
						}
					}
				}
				if !snake.Alive {
					continue
				}
				// 吃食物
				ate := false
				for i, food := range r.GameState.Foods {
					if food.X == newHead.X && food.Y == newHead.Y {
						snake.Segments = append([]SnakeSegment{newHead}, snake.Segments...)
						r.GameState.Foods[i] = r.randomFood()
						r.GameState.Scores[uid]++
						log.Printf("玩家 %s 吃到食物，得分 %d", uid, r.GameState.Scores[uid])
						ate = true
						break
					}
				}
				if !ate {
					snake.Segments = append([]SnakeSegment{newHead}, snake.Segments[:len(snake.Segments)-1]...)
				}
				aliveCount++
			}

			// 游戏状态变化标志
			stateChanged := false

			// 游戏结束判定
			if aliveCount <= 1 && len(r.GameState.Snakes) > 1 {
				r.GameState.Status = "gameover"
				stateChanged = true
				log.Printf("游戏结束，存活玩家数=%d", aliveCount)
			}

			r.mu.Unlock()

			// 广播游戏状态
			r.broadcastGameState()

			// 如果游戏结束，退出循环
			if stateChanged && r.GameState.Status == "gameover" {
				log.Printf("游戏状态变为gameover，退出游戏循环")
				return
			}

		case <-r.stopGame:
			log.Printf("收到停止游戏信号，退出游戏循环")
			return
		}
	}
}

func (r *Room) broadcastGameState() {
	r.gameMu.RLock()
	defer r.gameMu.RUnlock()

	if r.GameState == nil {
		log.Printf("游戏状态为空，不广播")
		return
	}

	// 创建消息
	msg := Message{
		Type:    MessageTypeGameState,
		Content: "",
		Time:    time.Now().Unix(),
	}

	// 序列化游戏状态
	data, err := json.Marshal(r.GameState)
	if err != nil {
		log.Printf("序列化游戏状态失败 | 房间ID=%s 错误详情: %+v", r.ID, err)
		return
	}
	msg.Content = string(data)

	// 记录广播信息
	log.Printf("广播游戏状态: 状态=%s, 玩家数=%d", r.GameState.Status, len(r.GameState.Snakes))

	// 广播消息
	r.broadcast(msg)
}

// 处理玩家操作
func (r *Room) handleGameOp(op SnakeGameOp) {
	r.gameMu.RLock()
	defer r.gameMu.RUnlock()

	log.Printf("处理游戏操作: 用户=%s 操作=%s | 元数据: %+v", op.UserID, op.Op, op)

	if r.GameState == nil {
		log.Printf("游戏状态为空，初始化游戏状态")
		// 初始化游戏状态
		r.GameState = &SnakeGameState{
			Snakes: make(map[string]*Snake),
			Foods:  []SnakeSegment{},
			Scores: make(map[string]int),
			Status: "waiting",
			Ready:  make(map[string]bool),
		}
	}

	switch op.Op {
	case "sync_state":
		// 客户端请求同步游戏状态
		log.Printf("收到同步游戏状态请求: 用户=%s", op.UserID)

		// 立即广播当前游戏状态
		if r.GameState != nil {
			r.broadcastGameState()
			log.Printf("已向用户 %s 同步游戏状态", op.UserID)
		} else {
			log.Printf("游戏状态为空，无法同步")
		}

	case "ready":
		// 确定准备状态值
		ready := true
		if op.Ready != nil {
			ready = *op.Ready
		}

		// 打印详细的操作信息，帮助调试
		log.Printf("收到准备操作 - 原始数据: %+v", op)
		log.Printf("玩家准备状态变更: 用户=%s, 准备=%v", op.UserID, ready)

		// 确保用户名存在
		userName := op.UserID
		if op.UserName != "" {
			userName = op.UserName
		} else if name, ok := r.UserNames[op.UserID]; ok {
			userName = name
		}

		// 更新GameState.Ready
		if r.GameState == nil {
			r.GameState = &SnakeGameState{
				Snakes: make(map[string]*Snake),
				Foods:  []SnakeSegment{},
				Scores: make(map[string]int),
				Ready:  make(map[string]bool),
				Status: "waiting",
			}
		}

		if r.GameState.Ready == nil {
			r.GameState.Ready = make(map[string]bool)
		}
		r.GameState.Ready[op.UserID] = ready

		// 更新Room.Ready
		if r.Ready == nil {
			r.Ready = make(map[string]bool)
		}
		r.Ready[op.UserID] = ready

		// 构建准备状态消息时包含用户名
		readyData := map[string]interface{}{
			"user_id":   op.UserID,
			"user_name": userName, // 使用确认后的用户名
			"ready":     ready,
		}
		jsonData, _ := json.Marshal(readyData)
		readyMsg := Message{
			Type:    MessageTypeReadyChanged,
			Content: string(jsonData),
			UserID:  op.UserID,
			Time:    time.Now().Unix(), // 添加时间戳
		}

		// 打印日志
		log.Printf("发送准备状态变更消息: 用户ID=%s, 用户名=%s, 准备状态=%v, 消息类型=%d, 内容=%s",
			op.UserID, userName, ready, MessageTypeReadyChanged, readyMsg.Content)

		// 立即广播准备状态变更消息
		r.broadcast(readyMsg)

		// 添加延迟，确保消息被处理
		time.Sleep(50 * time.Millisecond)

		// 检查是否所有人都准备好
		allReady := true
		for uid := range r.Clients {
			ready, exists := r.GameState.Ready[uid]
			if !exists || !ready {
				allReady = false
				break
			}
		}

		log.Printf("检查所有玩家是否准备: allReady=%v, 玩家数=%d",
			allReady, len(r.Clients))

		if allReady && len(r.Clients) > 1 {
			log.Printf("所有玩家已准备，开始游戏")
			go r.StartGame()
		}
	case "direction":
		snake, ok := r.GameState.Snakes[op.UserID]
		if ok && snake.Alive {
			// 防止反向
			if (op.Dir == "LEFT" && snake.Direction != "RIGHT") ||
				(op.Dir == "RIGHT" && snake.Direction != "LEFT") ||
				(op.Dir == "UP" && snake.Direction != "DOWN") ||
				(op.Dir == "DOWN" && snake.Direction != "UP") {
				snake.Direction = op.Dir
			}
		}
	case "start":
		log.Printf("玩家请求开始游戏: 用户=%s", op.UserID)
		// 发送游戏开始通知给所有玩家
		startNotification := Message{
			Type:     MessageTypeGameOp,
			UserID:   op.UserID,
			UserName: r.UserNames[op.UserID],
			Content:  fmt.Sprintf(`{"op":"start_notify","user_id":"%s","user_name":"%s"}`, op.UserID, r.UserNames[op.UserID]),
			Time:     time.Now().Unix(),
		}
		r.broadcast(startNotification)

		// 短暂延迟后开始游戏，确保通知先发送
		time.Sleep(100 * time.Millisecond)
		go r.StartGame()
	}
	r.broadcastGameState()
}

// 修改：WebSocket连接支持maxUsers参数，加入房间时校验人数
func (s *WebSocketServer) ServeWS(w http.ResponseWriter, r *http.Request, userID, roomID string) {
	userName := r.URL.Query().Get("user_name")
	if userName == "" {
		userName = userID
	}
	maxUsers := 20
	if v := r.URL.Query().Get("max_users"); v != "" {
		if n, err := strconv.Atoi(v); err == nil && n > 0 {
			maxUsers = n
		}
	}
	conn, err := s.upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Printf("WebSocket连接升级失败: %v", err)
		return
	}

	// 尝试获取房间
	room := s.getRoom(roomID)

	// 如果房间不存在，尝试创建或恢复
	if room == nil {
		log.Printf("房间 %s 不存在，尝试创建或恢复", roomID)

		// 判断roomID是否为数字
		if _, err := strconv.Atoi(roomID); err == nil {
			// 是数字，查数据库
			var dbRoom model.Room
			if err := s.DB.First(&dbRoom, roomID).Error; err == nil {
				// 数据库有，创建内存房间
				log.Printf("从数据库恢复房间 %s", roomID)
				room = &Room{
					ID:          fmt.Sprintf("%d", dbRoom.ID), // 确保ID是字符串
					Name:        dbRoom.Name,
					CreatorID:   dbRoom.CreatorID,
					CreatorName: dbRoom.CreatorName,
					Clients:     make(map[string]*Client),
					UserNames:   make(map[string]string),
					JoinTimes:   make(map[string]int64),
					Ready:       make(map[string]bool),
					MaxUsers:    maxUsers,
					wsServer:    s,
				}
				s.mu.Lock()
				s.Rooms[room.ID] = room
				s.mu.Unlock()

				log.Printf("房间 %s 已从数据库恢复", room.ID)
			} else {
				// 数据库没有，创建新房间并保存到数据库
				roomName := "chat" // 默认房间名
				// 根据URL判断是否为游戏房间
				referer := r.Header.Get("Referer")
				if strings.Contains(referer, "snake.html") {
					roomName = "snake"
					maxUsers = 2
				} else if strings.Contains(referer, "gomoku.html") {
					roomName = "gomoku"
					maxUsers = 2
				} else if strings.Contains(referer, "tictactoe.html") {
					roomName = "tictactoe"
					maxUsers = 2
				}

				// 创建数据库记录
				dbRoom = model.Room{
					Name:        roomName,
					CreatorID:   userID,
					CreatorName: userName,
					Tag:         "game",
				}
				if err := s.DB.Create(&dbRoom).Error; err != nil {
					log.Printf("创建房间数据库记录失败: %v", err)
					// 即使数据库创建失败，也尝试创建内存房间
					room = &Room{
						ID:          roomID,
						Name:        roomName,
						CreatorID:   userID,
						CreatorName: userName,
						Clients:     make(map[string]*Client),
						UserNames:   make(map[string]string),
						JoinTimes:   make(map[string]int64),
						Ready:       make(map[string]bool),
						MaxUsers:    maxUsers,
						wsServer:    s,
					}
					s.mu.Lock()
					s.Rooms[roomID] = room
					s.mu.Unlock()
					log.Printf("创建内存房间 %s (数据库创建失败)", roomID)
				} else {
					// 创建内存房间，使用数据库生成的ID
					room = &Room{
						ID:          fmt.Sprintf("%d", dbRoom.ID), // 使用数据库生成的ID
						Name:        roomName,
						CreatorID:   userID,
						CreatorName: userName,
						Clients:     make(map[string]*Client),
						UserNames:   make(map[string]string),
						JoinTimes:   make(map[string]int64),
						Ready:       make(map[string]bool),
						MaxUsers:    maxUsers,
						wsServer:    s,
					}
					s.mu.Lock()
					s.Rooms[room.ID] = room
					s.mu.Unlock()
					log.Printf("创建新房间 %s 并保存到数据库", room.ID)
				}
			}
		} else {
			// 不是数字，自动新建内存房间
			room = &Room{
				ID:          roomID,
				Name:        "snake", // 默认为snake游戏
				CreatorID:   userID,
				CreatorName: userName,
				Clients:     make(map[string]*Client),
				UserNames:   make(map[string]string),
				JoinTimes:   make(map[string]int64),
				Ready:       make(map[string]bool),
				MaxUsers:    maxUsers,
				wsServer:    s,
			}
			s.mu.Lock()
			s.Rooms[roomID] = room
			s.mu.Unlock()
			log.Printf("创建新的内存房间 %s", roomID)
		}
	}
	room.mu.RLock()
	full := len(room.Clients) >= room.MaxUsers
	room.mu.RUnlock()
	if full {
		conn.WriteMessage(websocket.TextMessage, []byte(`{"type":99,"content":"房间已满"}`))
		conn.Close()
		return
	}
	client := &Client{
		UserID:   userID,
		UserName: userName,
		Conn:     conn,
		Send:     make(chan []byte, 256),
		mu:       sync.Mutex{}, // 初始化互斥锁
	}
	room.addClient(client, s)

	// 延迟一小段时间，确保客户端准备就绪
	time.Sleep(100 * time.Millisecond)

	// 创建加入消息
	joinMsg := Message{
		Type:     MessageTypeJoin,
		UserID:   userID,
		UserName: userName,
		Content:  fmt.Sprintf("%s 加入了房间", userName),
		Time:     time.Now().Unix(),
	}

	// 启动客户端处理goroutine
	go client.writePump()
	go func() {
		// 捕获可能的panic
		defer func() {
			if r := recover(); r != nil {
				log.Printf("客户端处理goroutine恢复自panic: %v", r)
				// 确保客户端被移除
				room.removeClient(client, s)
			}
		}()
		client.readPumpWithServer(room, s)
	}()

	// 广播加入消息
	go room.broadcast(joinMsg)
}

// 新增 readPumpWithServer 方法，确保 removeClient 能拿到 wsServer
func (c *Client) readPumpWithServer(room *Room, wsServer *WebSocketServer) {
	defer func() {
		room.removeClient(c, wsServer)
	}()
	c.readPump(room, wsServer)
}

// 设置玩家准备状态
func (r *Room) setPlayerReady(userID string, ready bool) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if _, exists := r.Clients[userID]; exists {
		r.Ready[userID] = ready
	}
}

// 检查是否所有玩家都准备好了
func (r *Room) allPlayersReady() bool {
	r.mu.RLock()
	defer r.mu.RUnlock()
	if len(r.Clients) < 2 {
		return false
	}
	for userID := range r.Clients {
		if !r.Ready[userID] {
			return false
		}
	}
	return true
}
